/**
 * @file EcomCommonFunctionsBean.java
 * Projet eCOM 2010 (Miage Descartes)
 * @author Doido Sébastien
 * @version 2
 * @brief Bean relatif au traitement des opérations réalisables par un client ou un administrateur
 */
package ecom.beans;

import java.util.Collection;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

/**
 * @brief Bean relatif au traitement des opérations réalisables par un client ou un administrateur
 * @author Sebastien
 */
public class EcomCommonFunctionsBean {

    /**
     * @brief Gestionnaire de persistance
     */
    @PersistenceContext
    protected EntityManager em;

    /**
     * @brief Renvoie un compte pour un ID donné
     * @param id : numéro du compte demandé
     * @return le compte associé à l'id demandé
     * @throws ecom.beans.UnknownIdentifier : id inexistant
     */
    public AccountBean getAccountById(int idAccount) throws UnknownIdentifier {
        em.flush();
        AccountBean a = em.find(AccountBean.class, idAccount);
        if (a == null) {
            throw new UnknownIdentifier("Account id #" + idAccount + " is not known");
        } else {
            em.refresh(a);
        }
        return a;
    }

    /**
     * @brief Renvoie un compte pour un couple login/pass donné
     * @param login : identifiant à vérifier
     * @param pass : pass associé à l'identifiant
     * @return le compte associé au couple login/pass
     */
    public AccountBean getAccountByCredential(String login, String pass) {
        AccountBean result;
        try {
        Query account = em.createNamedQuery("authenticate");
        account.setParameter("login", login);
        account.setParameter("pass", pass);
        result = (AccountBean) account.getSingleResult();
        }
        catch(NoResultException e) {
            result = null;
        }
        return result;
    }

    /**
     * @brief Creation d'un compte acheteur/magasin
     * @param accountOwner : nom du propriétaire
     * @param balance : solde du compte
     * @param login : login pour l'accès aux opérations de gestion du compte
     * @param pass : mot de passe
     * @param surname : prénom du propriétaire
     * @param address : adresse du propriétaire
     * @param birthdate : date de naissance du propriétaire
     * @return l'identifiant affecté au compte
     */
    public int createAccount(String accountOwner, double balance, String login, String pass, String surname, String address, String birthdate) {
        System.out.println("creating account with auto id");
        AccountBean myAccount = new AccountBean(accountOwner, balance, login, pass, surname, address, birthdate);
        em.persist(myAccount);
        return myAccount.getAccountId();
    }

    /**
     * @brief Mise à jour d'un compte acheteur/magasin
     * @param accountId : id du compte à modifier
     * @param accountOwner : nom du propriétaire
     * @param balance : solde du compte
     * @param login : login pour l'accès aux opérations de gestion du compte
     * @param pass : mot de passe
     * @param surname : prénom du propriétaire
     * @param address : adresse du propriétaire
     * @param birthdate : date de naissance du propriétaire
     * @throws ecom.beans.UnknownIdentifier : exception levée lorsque compte inexistant
     */
    public void updateAccount(int accountId, String accountOwner, double balance, String login, String pass,
                              String surname, String address, String birthdate) throws UnknownIdentifier {
        AccountBean a = getAccountById(accountId);
        a.setAccountOwner(accountOwner);
        a.setBalance(balance);
        a.setLogin(login);
        a.setPass(pass);
        a.setSurname(surname);
        a.setAddress(address);
        a.setBirthdate(birthdate);
        em.persist(a);
    }
    
    /**
     * @brief Renvoie tous les produits connus
     * @return tous les produits connus
     */
    public Collection<ProductBean> getProducts() {
        System.out.println("loading all products");
        return em.createNamedQuery("getAllProducts").getResultList();
    }

    /**
     * @brief Renvoie un produit pour un id donné
     * @param idProduct : id demandé
     * @return un produit pour un id donné
     * @throws ecom.beans.UnknownIdentifier : référence produit inexistante
     */
    public ProductBean getProductById(int idProduct) throws UnknownIdentifier {
        ProductBean product = em.find(ProductBean.class, idProduct);
        if (product == null) {
            throw new UnknownIdentifier("Product id #" + idProduct + " is not known");
        }
        return product;
    }

    public Collection<ProductBean> getProductsInCateg(int idCateg) throws UnknownIdentifier {
        CategoryBean categ = em.find(CategoryBean.class, idCateg);

        Query productlist = em.createNamedQuery("getAllProductsInCateg");
        productlist.setParameter("categ", idCateg);
        return productlist.getResultList();
    }

    /**
     * @brief renvoie tous les magasins connus
     * @return tous les magasins connus
     */
    public Collection<ProductStoreBean> getStores() {
        System.out.println("loading all product stores");
        return em.createNamedQuery("getAllProductStores").getResultList();
    }

    /**
     * @brief Renvoie un magasin  pour un id donné
     * @param id : id demandé
     * @return un magasin  pour un id donné
     * @throws ecom.beans.UnknownIdentifier : : référence magasin inexistante
     */
    public ProductStoreBean getStoreById(int id) throws UnknownIdentifier {
        ProductStoreBean store = em.find(ProductStoreBean.class, id);
        if (store == null) {
            throw new UnknownIdentifier("Store id #" + id + " is not known");
        }
        return store;
    }

    /**
     * @brief Renvoie toutes les catégories connues
     * @return toutes les catégories connues
     */
    public Collection<CategoryBean> getCategories() {
        System.out.println("loading all categories");
        return em.createNamedQuery("getAllCategories").getResultList();
    }

    /**
     * @brief Renvoie une catégorie  pour un id donné
     * @param id : id demandé
     * @return une catégorie  pour un id donné
     * @throws ecom.beans.UnknownIdentifier : référence catégorie inexistante
     */
    public CategoryBean getCategoryById(int id)  throws UnknownIdentifier {
        CategoryBean categ = em.find(CategoryBean.class, id);
        if (categ == null) {
            throw new UnknownIdentifier("Category id #" + id + " is not known");
        }
        return categ;
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire connues
     * @return toutes les lignes d'inventaire connues
     */
    public Collection<WarehouseLineBean> getStocks() {
        return em.createNamedQuery("getAllStocks").getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire relatives à un magasin
     * @param productStoreId : référence magasin
     * @return toutes les lignes d'inventaire relatives à un magasin
     * @throws ecom.beans.UnknownIdentifier : id magasin inexistant
     */
    public Collection<WarehouseLineBean> getProductsInStore(int productStoreId) throws UnknownIdentifier {
        ProductStoreBean store = this.getStoreById(productStoreId);
        
        Query productlist = em.createNamedQuery("getProductsInStore");
        productlist.setParameter("productStoreID", store.getReference());
        return productlist.getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire relatives à un produit
     * @param productId : référence produit
     * @return toutes les lignes d'inventaire relatives à un produit
     * @throws ecom.beans.UnknownIdentifier : id produi inexistant
     */
    public Collection<WarehouseLineBean> getProductsByProductId(int productId) throws UnknownIdentifier {
        ProductBean product = this.getProductById(productId);

        Query productlist = em.createNamedQuery("getProductsByProductId");
        productlist.setParameter("productID", product.getProductId());
        return productlist.getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire relatives à un produit dont le nom est 'name'
     * @param name : nom du produit recherché
     * @return toutes les lignes d'inventaire relatives à un produit dont le nom est 'name'
     */
    public Collection<WarehouseLineBean> getProductsByName(String name) {
        Query productlist = em.createNamedQuery("getProductByName");
        productlist.setParameter("name", name);
        return productlist.getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire relatives à un produit dont le nom similaire à 'name'
     * @param name : critère de similitude
     * @return toutes les lignes d'inventaire relatives à un produit dont le nom similaire à 'name'
     */
    public Collection<WarehouseLineBean> getProductsBySimilarName(String name) {
        Query productlist = em.createNamedQuery("getProductByNameLike");
        productlist.setParameter("name", name);
        return productlist.getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire pour un prix de vente compris entre 'min' et 'max'
     * @param min : prix min (eur)
     * @param max : prix max (eur)
     * @return  toutes les lignes d'inventaire pour un prix de vente compris entre 'min' et 'max'
     */
    public Collection<WarehouseLineBean> getProductsByPriceInterval(double min, double max) {
        Query productlist = em.createNamedQuery("getProductByPriceInterval");
        productlist.setParameter("min", min);
        productlist.setParameter("max", max);
        return productlist.getResultList();
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire pour un prix de vente < 'max'
     * @param max : prix max (eur)
     * @return  toutes les lignes d'inventaire pour un prix de vente < 'max'
     */
    public Collection<WarehouseLineBean> getProductsByByMaximumPrice(double max) {
        return getProductsByPriceInterval(0, max);
    }

    /**
     * @brief Renvoie toutes les lignes d'inventaire pour un produit de catégorie 'idCateg'
     * @param idCateg : référence catégorie
     * @return toutes les lignes d'inventaire pour un produit de catégorie 'idCateg'
     * @throws ecom.beans.UnknownIdentifier : catégorie inexistante
     */
    public Collection<WarehouseLineBean> getProductsByCategory(int idCateg)  throws UnknownIdentifier {
        CategoryBean categ = this.getCategoryById(idCateg);
        em.flush();
        em.clear();
        Query productlist = em.createNamedQuery("getProductByCategory");
        productlist.setParameter("categ", categ.getIdCateg());
        return productlist.getResultList();
    }

    /**
     * @brief Suppression de produits (quantité) d'une ligne de stock
     * @param productStoreId : magasin vendant le produit
     * @param productId : produit vendu
     * @param quantity : quantité à supprimer
     * @throws ecom.beans.UnknownIdentifier : magasin ou produit inconnu
     * @throws java.lang.Exception : erreur de suppression (quantité >max)
     */
    public void removeStockLine(int productStoreId, int productId, int quantity) throws UnknownIdentifier, Exception {
        ProductStoreBean store = getStoreById(productStoreId);
        ProductBean product = getProductById(productId);

        Query stockList = em.createNamedQuery("getStockByProductAndStore");
        stockList.setParameter("productID", product.getProductId());
        stockList.setParameter("productStoreID", store.getReference());

        try {
            WarehouseLineBean myStock = (WarehouseLineBean) stockList.getSingleResult();

            if (quantity > myStock.getQuantity()) {
                throw new Exception("\t Impossible to remove '" + quantity + "' product #" +
                        product.getProductId() + " for Store #" + store.getReference() +
                        " (max removable : " + myStock.getQuantity() + ")");
            } else if (myStock.getQuantity() == quantity) {
                em.remove(myStock);
            } else {
                myStock.setQuantity(myStock.getQuantity() - quantity);
                em.persist(myStock);
            }
        } catch (NoResultException e) {
            throw new UnknownIdentifier("/!\\ StockLine for product #" +
                    product.getProductId() + " and store #" +
                    store.getReference() + " is not known");
        }
    }
}
